package org.dxy.domain.command.workorder;

import lombok.Builder;
import org.dxy.domain.command.Command;
import org.dxy.types.enums.WorkOrderTypeEnum;

import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * 创建工单命令
 * <p>
 * 该命令用于创建各种类型的工单，包括：
 * <ul>
 *   <li>标准生产工单：正常的生产计划</li>
 *   <li>返工工单：针对不合格品的重新加工</li>
 *   <li>试产工单：研发阶段的小批量试制</li>
 *   <li>委外工单：外协加工工单</li>
 *   <li>维修工单：设备或产品维修</li>
 *   <li>样品工单：打样、送检用途</li>
 *   <li>工程变更工单：因设计变更触发的工单</li>
 * </ul>
 * 
 * @author dxy
 * @since 1.0.0
 */
@Builder
public record CreateWorkOrderCommand(
        /** 
         * 工单编码
         * <p>格式要求：WO-YYYYMMDD-NNN（如：WO-20241201-001）
         * <p>必须唯一，不能为空
         */
        String workOrderCode,
        
        /** 
         * 产品ID
         * <p>必须是有效的产品ID，不能为空且必须大于0
         */
        Long productId,
        
        /** 
         * 计划数量
         * <p>必须大于0的整数
         */
        Integer plannedQuantity,
        
        /** 
         * 工艺路线ID
         * <p>必须是有效的工艺路线ID，不能为空
         */
        Long routeId,
        
        /** 
         * 产线ID
         * <p>必须是有效的产线ID，不能为空
         */
        Long productionLineId,
        
        /** 
         * 工单类型
         * <p>不能为空，决定了工单的处理流程和业务规则
         * @see WorkOrderTypeEnum
         */
        WorkOrderTypeEnum workOrderType,
        
        /** 
         * 原工单ID（返工工单时必填）
         * <p>当工单类型为 REWORK 时，此字段必填
         * <p>用于追溯返工的原始工单
         */
        Long originalWorkOrderId,
        
        /** 
         * 计划开始时间（可选）
         * <p>如果指定，不能早于当前时间
         * <p>为空时表示可立即开始
         */
        LocalDate plannedStartTime,
        
        /** 
         * 计划结束时间（可选）
         * <p>如果指定，必须晚于计划开始时间
         * <p>用于生产计划和排程
         */
        LocalDate  plannedEndTime
) implements Command {
    
    /**
     * 验证命令参数的有效性
     * <p>该方法会在命令处理前被调用，确保所有必要的业务规则得到满足
     * 
     * @throws IllegalArgumentException 当参数不符合业务规则时
     */
    public void validate() {
        // 1. 验证工单编码
        if (workOrderCode == null || workOrderCode.trim().isEmpty()) {
            throw new IllegalArgumentException("工单编码不能为空");
        }
        
        // 2. 验证产品ID
        if (productId == null || productId <= 0) {
            throw new IllegalArgumentException("产品ID必须大于0");
        }
        
        // 3. 验证计划数量
        if (plannedQuantity == null || plannedQuantity <= 0) {
            throw new IllegalArgumentException("计划数量必须大于0");
        }
        
        // 4. 验证工单类型
        if (workOrderType == null) {
            throw new IllegalArgumentException("工单类型不能为空");
        }
        
        // 5. 验证返工工单的原工单ID
        if (workOrderType == WorkOrderTypeEnum.REWORK && originalWorkOrderId == null) {
            throw new IllegalArgumentException("返工工单必须指定原工单ID");
        }
        
        // 6. 验证工艺路线ID
        if (routeId == null || routeId <= 0) {
            throw new IllegalArgumentException("工艺路线ID不能为空且必须大于0");
        }
        
        // 7. 验证产线ID
        if (productionLineId == null || productionLineId <= 0) {
            throw new IllegalArgumentException("产线ID不能为空且必须大于0");
        }
        
        if (originalWorkOrderId != null && originalWorkOrderId <= 0) {
            throw new IllegalArgumentException("原工单ID必须大于0");
        }
        
        // 8. 验证时间逻辑
        if (plannedStartTime != null && plannedEndTime != null) {
            if (plannedEndTime.isBefore(plannedStartTime) || plannedEndTime.isEqual(plannedStartTime)) {
                throw new IllegalArgumentException("计划结束时间必须晚于计划开始时间");
            }
        }
    }
    
    /**
     * 判断是否为返工工单
     * 
     * @return true 如果是返工工单
     */
    public boolean isReworkOrder() {
        return workOrderType == WorkOrderTypeEnum.REWORK;
    }
    
    /**
     * 判断是否指定了工艺路线
     * 
     * @return true 如果指定了工艺路线
     */
    public boolean hasRoute() {
        return routeId != null && routeId > 0;
    }
    
    /**
     * 判断是否指定了产线
     * 
     * @return true 如果指定了产线
     */
    public boolean hasProductionLine() {
        return productionLineId != null && productionLineId > 0;
    }
    
    /**
     * 判断是否设置了计划时间
     * 
     * @return true 如果设置了计划开始或结束时间
     */
    public boolean hasPlannedTime() {
        return plannedStartTime != null || plannedEndTime != null;
    }
}